package com.kinyx.framework.db.daos;

import java.sql.SQLException;

import org.junit.Assert;
import org.junit.Test;

import com.kinyx.framework.db.configs.DBConfigsHelper;
import com.kinyx.framework.db.sentence.res.Condition;
import com.kinyx.framework.db.sentence.res.KDELETE;
import com.kinyx.framework.db.sentence.res.KINSERT;
import com.kinyx.framework.db.sentence.res.KSQLBuilder;
import com.kinyx.framework.db.sentence.res.KUPDATE;
import com.kinyx.framework.db.sentence.str.SQL;
import com.kinyx.framework.db.sentence.str.SQLBuilder;
import com.kinyx.framework.enums.impl.ErrorCode1;
import com.kinyx.framework.enums.impl.ErrorCodeX;
import com.kinyx.framework.enums.impl.UsingStatus;
import com.kinyx.framework.exception.KRuntimeException;
import com.kinyx.framework.utils.KDateTimeUtils;
import com.kinyx.test.entities.impl.DemoEntityR;

@SuppressWarnings("deprecation")
public abstract class JDBCDao_Tester_change extends SUPER {

	private String id1 = "1";
	private String id2 = "2";

	@Test
	public void testSuccess1() {
		this.logger.debug("insert using str.SQL");
		final int cnt1 = this.dao.insert(null, new SQL().x("INSERT INTO T_DEMO (ID, USING_STATUS, INSERT_TIME, UPDATE_TIME) VALUES (?, ?, ?, ?)", //
				this.id1, //
				UsingStatus.USING, //
				KDateTimeUtils.now("yyyyMMddHHmmssSSS"), //
				KDateTimeUtils.now("yyyyMMddHHmmssSSS")//
		));
		Assert.assertTrue(cnt1 == 1);

		this.logger.debug("update using str.SQL");
		final int cnt2 = this.dao.update(null, new SQL().x("UPDATE T_DEMO SET ID = ? WHERE ID = ?", this.id2, this.id1));
		Assert.assertTrue(cnt2 == 1);

		this.logger.debug("delete using str.SQL");
		final int cnt3 = this.dao.delete(null, new SQL().x("DELETE FROM T_DEMO WHERE ID = ?", this.id2));
		Assert.assertTrue(cnt3 == 1);
	}

	@Test
	public void testSuccess2() {
		this.logger.debug("insert using str.INSERT");
		final int cnt1 = this.dao.insert(null, SQLBuilder.insert("T_DEMO")//
				.values("ID", this.id1)//
				.values("USING_STATUS", UsingStatus.USING)//
				.values("INSERT_TIME", KDateTimeUtils.now("yyyyMMddHHmmssSSS"))//
				.values("UPDATE_TIME", KDateTimeUtils.now("yyyyMMddHHmmssSSS"))//
		);
		Assert.assertTrue(cnt1 == 1);

		this.logger.debug("update using str.UPDATE");
		final int cnt2 = this.dao.update(null, SQLBuilder.update("T_DEMO").set("ID", this.id2).where(new SQL().x("ID = ?", this.id1)));
		Assert.assertTrue(cnt2 == 1);

		this.logger.debug("delete using str.DELETE");
		final int cnt3 = this.dao.delete(null, SQLBuilder.delete("T_DEMO").where(new SQL().x("ID = ?", this.id2)));
		Assert.assertTrue(cnt3 == 1);
	}

	@Test
	public void testSuccess3() {
		this.logger.debug("insert using res.INSERT");
		final KINSERT insert = KSQLBuilder.insert(DemoEntityR.class)//
				.values(DemoEntityR.id, this.id1)//
				.values(DemoEntityR.usingStatus, UsingStatus.USING)//
				.values(DemoEntityR.insertTime, KDateTimeUtils.now("yyyyMMddHHmmssSSS"))//
				.values(DemoEntityR.updateTime, KDateTimeUtils.now("yyyyMMddHHmmssSSS"));
		final int cnt1 = this.dao.insert(null, insert);
		Assert.assertTrue(cnt1 == 1);

		this.logger.debug("update using res.UPDATE");
		final KUPDATE update = KSQLBuilder.update(DemoEntityR.class).set(DemoEntityR.id, this.id2).where(Condition.eq(DemoEntityR.id, this.id1));
		final int cnt2 = this.dao.update(null, update);
		Assert.assertTrue(cnt2 == 1);

		this.logger.debug("delete using res.DELETE");
		final KDELETE delete = KSQLBuilder.delete(DemoEntityR.class).where(Condition.eq(DemoEntityR.id, this.id2));
		final int cnt3 = this.dao.delete(null, delete);
		Assert.assertTrue(cnt3 == 1);
	}

	@Test
	public void testError11() {
		final SQL sql = null;
		this.logger.debug("testError11 空指针（尚未连接数据库）");
		Assert.assertThrows(NullPointerException.class, () -> { this.dao.insert(null, sql); });
		Assert.assertThrows(NullPointerException.class, () -> { this.dao.update(null, sql); });
		Assert.assertThrows(NullPointerException.class, () -> { this.dao.delete(null, sql); });
	}

	@Test
	public void testError12() {
		this.logger.debug("testError12 SQL语句基础校验，必须以XXX开头（尚未连接数据库）");
		Assert.assertThrows(Exception.class, () -> {
			try {
				this.dao.insert(null, new SQL());
			} catch (final Exception e) {
				Assert.assertTrue(e instanceof KRuntimeException);
				Assert.assertEquals(((KRuntimeException) e).getErrorCode(), ErrorCode1.CODE_ERROR_804);
				throw e;
			}
		});

		Assert.assertThrows(Exception.class, () -> {
			try {
				this.dao.update(null, new SQL());
			} catch (final Exception e) {
				Assert.assertTrue(e instanceof KRuntimeException);
				Assert.assertEquals(((KRuntimeException) e).getErrorCode(), ErrorCode1.CODE_ERROR_804);
				throw e;
			}
		});

		Assert.assertThrows(Exception.class, () -> {
			try {
				this.dao.delete(null, new SQL());
			} catch (final Exception e) {
				Assert.assertTrue(e instanceof KRuntimeException);
				Assert.assertEquals(((KRuntimeException) e).getErrorCode(), ErrorCode1.CODE_ERROR_804);
				throw e;
			}
		});
	}

	@Test
	public void testError13() {
		this.logger.debug("testError13 SQL语句基础校验，必须以XXX开头（尚未连接数据库）");
		Assert.assertThrows(Exception.class, () -> {
			try {
				this.dao.insert(null, new SQL().s("SELECT"));
			} catch (final Exception e) {
				Assert.assertTrue(e instanceof KRuntimeException);
				Assert.assertEquals(((KRuntimeException) e).getErrorCode(), ErrorCode1.CODE_ERROR_804);
				throw e;
			}
		});

		Assert.assertThrows(Exception.class, () -> {
			try {
				this.dao.update(null, new SQL().s("SELECT"));
			} catch (final Exception e) {
				Assert.assertTrue(e instanceof KRuntimeException);
				Assert.assertEquals(((KRuntimeException) e).getErrorCode(), ErrorCode1.CODE_ERROR_804);
				throw e;
			}
		});

		Assert.assertThrows(Exception.class, () -> {
			try {
				this.dao.delete(null, new SQL().s("SELECT"));
			} catch (final Exception e) {
				Assert.assertTrue(e instanceof KRuntimeException);
				Assert.assertEquals(((KRuntimeException) e).getErrorCode(), ErrorCode1.CODE_ERROR_804);
				throw e;
			}
		});
	}

	@Test
	public void testError9() {
		this.logger.debug("testError9 未设置主键（已经在数据库中执行）");
		Assert.assertThrows(Exception.class, () -> {
			try {
				this.dao.insert(null, new SQL().x("INSERT INTO T_DEMO (USING_STATUS) VALUES (?)", UsingStatus.USING));
			} catch (final Exception e) {
				Assert.assertTrue(e instanceof KRuntimeException);
				Assert.assertEquals(((KRuntimeException) e).getErrorCode(), ErrorCode1.CODE_ERROR_108);
				Assert.assertTrue(e.getCause() instanceof SQLException);
				switch (DBConfigsHelper.getDBConfigs(this.dao.getKey()).getDialect().name()) {
					case "MySQL":
						Assert.assertTrue(e.getCause().getMessage().startsWith("Field 'ID' doesn't have a default value"));
						break;
					case "Oracle":
						Assert.assertTrue(e.getCause().getMessage().startsWith("ORA-01400: 无法将 NULL 插入"));
						break;
					default :
						throw new KRuntimeException(ErrorCodeX.NotImplemented);
				}
				throw e;
			}
		});
	}

	@Test
	public void testErrorA0() {
		this.logger.debug("testErrorA0 新增相同主键数据，导致主键冲突。（已经在数据库中执行）");
		final int cnt1 = this.dao.insert(null, new SQL().x("INSERT INTO T_DEMO (ID, USING_STATUS, INSERT_TIME, UPDATE_TIME) VALUES (?, ?, ?, ?)", //
				"1", //
				UsingStatus.USING, //
				KDateTimeUtils.now("yyyyMMddHHmmssSSS"), //
				KDateTimeUtils.now("yyyyMMddHHmmssSSS")//
		));
		Assert.assertTrue(cnt1 == 1);
		Assert.assertThrows(Exception.class, () -> {
			try {
				this.dao.insert(null, new SQL().x("INSERT INTO T_DEMO (ID, USING_STATUS, INSERT_TIME, UPDATE_TIME) VALUES (?, ?, ?, ?)", //
						"1", //
						UsingStatus.USING, //
						KDateTimeUtils.now("yyyyMMddHHmmssSSS"), //
						KDateTimeUtils.now("yyyyMMddHHmmssSSS")//
				));
			} catch (final Exception e) {
				Assert.assertTrue(e instanceof KRuntimeException);
				Assert.assertEquals(((KRuntimeException) e).getErrorCode(), ErrorCode1.CODE_ERROR_108);
				Assert.assertTrue(e.getCause() instanceof SQLException);
				switch (DBConfigsHelper.getDBConfigs(this.dao.getKey()).getDialect().name()) {
					case "MySQL":
						Assert.assertTrue(e.getCause().getMessage().startsWith("Duplicate entry"));
						break;
					case "Oracle":
						Assert.assertTrue(e.getCause().getMessage().startsWith("ORA-00001: 违反唯一约束条件"));
						break;
					default :
						throw new KRuntimeException(ErrorCodeX.NotImplemented);
				}
				this.dao.delete(null, new SQL().x("DELETE FROM T_DEMO WHERE ID = ?", "1"));
				throw e;
			}
		});
	}

	@Test
	public void testErrorA1() {
		this.logger.debug("testErrorA1 修改成相同主键，导致主键冲突。（已经在数据库中执行）");
		final int cnt1 = this.dao.insert(null, new SQL().x("INSERT INTO T_DEMO (ID, USING_STATUS, INSERT_TIME, UPDATE_TIME) VALUES (?, ?, ?, ?)", //
				"1", //
				UsingStatus.USING, //
				KDateTimeUtils.now("yyyyMMddHHmmssSSS"), //
				KDateTimeUtils.now("yyyyMMddHHmmssSSS")//
		));
		Assert.assertTrue(cnt1 == 1);
		final int cnt2 = this.dao.insert(null, new SQL().x("INSERT INTO T_DEMO (ID, USING_STATUS, INSERT_TIME, UPDATE_TIME) VALUES (?, ?, ?, ?)", //
				"2", //
				UsingStatus.USING, //
				KDateTimeUtils.now("yyyyMMddHHmmssSSS"), //
				KDateTimeUtils.now("yyyyMMddHHmmssSSS")//
		));
		Assert.assertTrue(cnt2 == 1);

		Assert.assertThrows(Exception.class, () -> {
			try {
				this.dao.update(null, new SQL().x("UPDATE T_DEMO SET ID = ?", "1").x(" WHERE ID = ?", "2"));
			} catch (final Exception e) {
				Assert.assertTrue(e instanceof KRuntimeException);
				Assert.assertEquals(((KRuntimeException) e).getErrorCode(), ErrorCode1.CODE_ERROR_108);
				Assert.assertTrue(e.getCause() instanceof SQLException);
				switch (DBConfigsHelper.getDBConfigs(this.dao.getKey()).getDialect().name()) {
					case "MySQL":
						Assert.assertTrue(e.getCause().getMessage().startsWith("Duplicate entry"));
						break;
					case "Oracle":
						Assert.assertTrue(e.getCause().getMessage().startsWith("ORA-00001: 违反唯一约束条件"));
						break;
					default :
						throw new KRuntimeException(ErrorCodeX.NotImplemented);
				}
				this.dao.delete(null, new SQL().x("DELETE FROM T_DEMO WHERE ID = ?", "1"));
				this.dao.delete(null, new SQL().x("DELETE FROM T_DEMO WHERE ID = ?", "2"));
				throw e;
			}
		});
	}

}
